
#include "sync_source.h"
#include "common.h"
#include "fifo.h"
#include "gpio.h"
#include "irq.h"
#include "pin_def.h"
#include <linux/atomic.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <asm/io.h>

#define TRIGGER_PIN_ADDR 0x0c303040
static struct kfifo *fifo = NULL;
static unsigned irq = 0;

static atomic_t started = ATOMIC_INIT(0);
static atomic_t enable = ATOMIC_INIT(0);
static atomic_t counter = ATOMIC_INIT(0);

static irqreturn_t sync_handler(int irq, void *dev) 
{
	int ret;
	struct sync_counter_frame frame;
	frame.time_ns = ktime_get_ns();

	if (!atomic_read_acquire(&enable)) {
		// not enable
		PR_WARN("sync source receive sync pulse before enable");
		return IRQ_HANDLED;
	}
	// source is enabled
	frame.count = atomic_fetch_inc(&counter);
	ret = kfifo_in(fifo, &frame, sizeof(struct sync_counter_frame));
	if (ret == 0) {
		PR_ERR("sync source write kfifo failed, kfifo full");
	}

	return IRQ_HANDLED;
}

int setup_trigger_pin(void) 
{
	uint32_t value;
	uint32_t *addr;
	addr = ioremap(TRIGGER_PIN_ADDR, sizeof(uint32_t));
	value = readl(addr);
	PR_INFO("TRIGGER PIN reg value: 0x%x", value);
	writew(0xC404, addr);
	return 0;
}

int set_trigger_pin_low(void) 
{
	uint32_t value;
	uint32_t *addr;
	addr = ioremap(TRIGGER_PIN_ADDR, sizeof(uint32_t));
	value = readl(addr);
	PR_INFO("TRIGGER PIN reg value: 0x%x", value);
	writew(0xC404, addr);
	return 0;
}

int set_trigger_pin_high(void) 
{
	uint32_t value;
	uint32_t *addr;
	addr = ioremap(TRIGGER_PIN_ADDR, sizeof(uint32_t));
	value = readl(addr);
	PR_INFO("TRIGGER PIN reg value: 0x%x", value);
	writew(0xC408, addr);
	return 0;
}

int setup_sync_source(struct kfifo *_fifo)
{

	int ret = 0;

	fifo = _fifo;

	// request trigger pin gpio
	PR_INFO("request trigger pin");
	ret = request_gpio(TRIGGER_GPIO_ID, TRIGGER_GPIO_NAME);
	if (ret != 0) {
		PR_ERR("request trigger gpio failed, errno: %d", ret);
		fifo = NULL;
		return ret;
	}

	// set trigger pin to output low
	PR_INFO("set trigger pin to high");
	// ret = gpio_direction_output(TRIGGER_GPIO_ID, 0);
	// setup_trigger_pin();
	ret = set_trigger_pin_low();
	if (ret != 0) {
		PR_ERR("setup gpio direction failed, errno: %d", ret);
		fifo = NULL;
		return ret;
	}

	// request sync pin gpio
	PR_INFO("request pin (%d, %s)", SYNC_GPIO_ID, SYNC_GPIO_NAME);
	ret = request_gpio(SYNC_GPIO_ID, SYNC_GPIO_NAME);
	if (ret != 0) {
		PR_ERR("setup sync pin gpio failed, errno: %d", ret);
		fifo = NULL;
		return ret;
	}

	// set sync pin to input
	PR_INFO("set pin (%d, %s) to input", SYNC_GPIO_ID, SYNC_GPIO_NAME);
	ret = gpio_direction_input(SYNC_GPIO_ID);
	if (ret != 0) {
		PR_ERR("set sync gpio direction to input failed, errno: %d", ret);
		fifo = NULL;
		return ret;
	}

	// setup sync pin interrupt
	irq = gpio_to_irq(SYNC_GPIO_ID);
	
	ret = setup_irq(
		irq,
		sync_handler,
		IRQF_TRIGGER_FALLING, "sync_pin", NULL
	);

	if (ret != 0) {
		PR_ERR("setup sync pin irq failed, errno: %d", ret);
		fifo = NULL;
		return ret;
	}

	return ret;
}


void start_sync_source()
{
	int ret = 0;
	if (fifo == NULL) {
		return;
	}
	ret = atomic_cmpxchg(&started, 0, 1);
	if (ret != 0) {
		PR_WARN("sync source already started");
		return;
	}
	atomic_xchg(&counter, 0);
	atomic_xchg(&enable, 1);
	// atomic_set_release(&counter, 0);
	// atomic_set_release(&enable, 1);
	PR_INFO("enable trigger gpio pin");
	// setup trigger gpio HIGH
	// gpio_set_value(TRIGGER_GPIO_ID, 1);
	set_trigger_pin_high();
}

void stop_sync_source()
{
	int ret = 0;
	if (fifo == NULL) {
		return;
	}
	ret = atomic_cmpxchg(&started, 1, 0);
	if (ret != 1) {
		PR_WARN("sync source already stopped");
		return;
	}
	PR_INFO("disable trigger gpio pin");
	// setup trigger gpio LOW
	// gpio_set_value(TRIGGER_GPIO_ID, 0);
	set_trigger_pin_low();
	atomic_xchg(&enable, 0);
	atomic_xchg(&counter, 0);
	// atomic_set_release(&enable, 0);
	// atomic_set_release(&counter, 0);
}


